#include "D3D11Pad.h"
#include "Texture.h"
#include "TextureMgr.h"
#include "Scene.h"

struct FontCache {
    int height;
    int orient;
    bool prop;
    char face[32];
    oapi::Font::Style style;
    class D3D11Text *pFont;
    HFONT hFont;
} fcache[128];

int nfcache = 0;
int pens_allocated = 0;
int brushes_allocated = 0;
int fonts_allocated = 0;

oapi::Font *def_Font = NULL;
oapi::Pen *def_Pen = NULL;

#pragma region static
ID3D11InputLayout
    *D3D11Pad::IL_D3DXVECTOR3    = NULL;

ID3D11Buffer
    *D3D11Pad::Circle_low        = NULL,
    *D3D11Pad::Circle_high        = NULL,
    *D3D11Pad::cb_VS            = NULL,
    *D3D11Pad::cb_PS            = NULL,
    *D3D11Pad::VB                = NULL,
    *D3D11Pad::IB                = NULL;

ID3D11VertexShader
    *D3D11Pad::VS_Ellipse        = NULL,
    *D3D11Pad::VS_WideRect        = NULL,
    *D3D11Pad::VS_Line            = NULL,
    *D3D11Pad::VS_Fill            = NULL;

ID3D11PixelShader
    *D3D11Pad::PS_Draw            = NULL,
    *D3D11Pad::PS_Fill            = NULL;

const UINT
    D3D11Pad::SkpStride = 12,
    D3D11Pad::VBOffset = 0;

UINT
    D3D11Pad::vb_size = 34,
    D3D11Pad::ib_size = 96;

const float    D3D11Pad::BlendFactors[4] = { 1.0f };

void D3D11Pad::GlobalInit() {
//shaders:
    HRESULT hr;
    ID3DBlob *SBlob = NULL, *EBlob = NULL;

    D3D11_INPUT_ELEMENT_DESC ldesc[ ] = {
        { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 } };

    CompileFromFile( "Modules\\D3D11Shaders\\D3D11Pad.fx", NULL, NULL, "VS_Ellipse", vs_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
    ShowShaderCompilationError( hr, EBlob );
    HR( gd3dDevice->CreateInputLayout( ldesc, 1, SBlob->GetBufferPointer(), SBlob->GetBufferSize(), &IL_D3DXVECTOR3 ) );
    HR( gd3dDevice->CreateVertexShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &VS_Ellipse ) );
    REL( SBlob );
    REL( EBlob );

    CompileFromFile( "Modules\\D3D11Shaders\\D3D11Pad.fx", NULL, NULL, "VS_WideRect", vs_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
    ShowShaderCompilationError( hr, EBlob );
    HR( gd3dDevice->CreateVertexShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &VS_WideRect ) );
    REL( SBlob );
    REL( EBlob );

    CompileFromFile( "Modules\\D3D11Shaders\\D3D11Pad.fx", NULL, NULL, "VS_Line", vs_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
    ShowShaderCompilationError( hr, EBlob );
    HR( gd3dDevice->CreateVertexShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &VS_Line ) );
    REL( SBlob );
    REL( EBlob );

    CompileFromFile( "Modules\\D3D11Shaders\\D3D11Pad.fx", NULL, NULL, "VS_Fill", vs_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
    ShowShaderCompilationError( hr, EBlob );
    HR( gd3dDevice->CreateVertexShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &VS_Fill ) );
    REL( SBlob );
    REL( EBlob );

    CompileFromFile( "Modules\\D3D11Shaders\\D3D11Pad.fx", NULL, NULL, "PS_Draw", ps_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
    ShowShaderCompilationError( hr, EBlob );
    HR( gd3dDevice->CreatePixelShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &PS_Draw ) );
    REL( SBlob );
    REL( EBlob );

    CompileFromFile( "Modules\\D3D11Shaders\\D3D11Pad.fx", NULL, NULL, "PS_Fill", ps_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
    ShowShaderCompilationError( hr, EBlob );
    HR( gd3dDevice->CreatePixelShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &PS_Fill ) );
    REL( SBlob );
    REL( EBlob );

//buffers:
    D3D11_BUFFER_DESC bdesc;
    ZeroMemory( &bdesc, sizeof(bdesc) );
    bdesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    bdesc.ByteWidth = 34*12;
    bdesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    bdesc.MiscFlags = 0;
    bdesc.StructureByteStride = 0;
    bdesc.Usage = D3D11_USAGE_DYNAMIC;

    HR( gd3dDevice->CreateBuffer( &bdesc, NULL, &VB ) );
    vb_size = 34;

    ZeroMemory( &bdesc, sizeof(bdesc) );
    bdesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
    bdesc.ByteWidth = 2*32*3;
    bdesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    bdesc.MiscFlags = 0;
    bdesc.StructureByteStride = 0;
    bdesc.Usage = D3D11_USAGE_DYNAMIC;

    HR( gd3dDevice->CreateBuffer( &bdesc, NULL, &IB ) );
    ib_size = 96;

//Circle buffers
    float angle = 0.0f, step = (float)PI2 / 15.0f;
    D3DXVECTOR3 Vtx[65];
    UINT j;

//Circle_low:
    Vtx[0] = D3DXVECTOR3( 0.5f, 0.5f, 0.0f );
    for( j = 1; j < 17; j++ ) {
        Vtx[j].x = sin( angle )*0.5f + 0.5f;
        Vtx[j].y = cos( angle )*0.5f + 0.5f;
        Vtx[j].z = angle;
        angle += step;
    }

//    D3D11_BUFFER_DESC bdesc;
    ZeroMemory( &bdesc, sizeof(bdesc) );
    bdesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    bdesc.ByteWidth = 17*12;
    bdesc.CPUAccessFlags = 0;
    bdesc.MiscFlags = 0;
    bdesc.StructureByteStride = 0;
    bdesc.Usage = D3D11_USAGE_IMMUTABLE;

    D3D11_SUBRESOURCE_DATA sdata;
    ZeroMemory( &sdata, sizeof(sdata) );
    sdata.pSysMem = Vtx;

    HR( gd3dDevice->CreateBuffer( &bdesc, &sdata, &Circle_low ) );

//Circle_high:
    angle = 0.0f;
    step = (float)PI2 / 63.0f;

    Vtx[0] = D3DXVECTOR3( 0.5f, 0.5f, 0.0f );
    for( j = 1; j < 65; j++ ) {
        Vtx[j].x = sin( angle )*0.5f + 0.5f;
        Vtx[j].y = cos( angle )*0.5f + 0.5f;
        Vtx[j].z = angle;
        angle += step;
    }

    ZeroMemory( &bdesc, sizeof(bdesc) );
    bdesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    bdesc.ByteWidth = 65*12;
    bdesc.CPUAccessFlags = 0;
    bdesc.MiscFlags = 0;
    bdesc.StructureByteStride = 0;
    bdesc.Usage = D3D11_USAGE_IMMUTABLE;

    ZeroMemory( &sdata, sizeof(sdata) );
    sdata.pSysMem = Vtx;

    HR( gd3dDevice->CreateBuffer( &bdesc, &sdata, &Circle_high ) );

//constant buffer:
    ZeroMemory( &bdesc, sizeof(bdesc) );
    bdesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
    bdesc.ByteWidth = 80;
    bdesc.CPUAccessFlags = 0;
    bdesc.MiscFlags = 0;
    bdesc.StructureByteStride = 0;
    bdesc.Usage = D3D11_USAGE_DEFAULT;

    HR( gd3dDevice->CreateBuffer( &bdesc, NULL, &cb_VS ) );

    ZeroMemory( &bdesc, sizeof(bdesc) );
    bdesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
    bdesc.ByteWidth = 32;
    bdesc.CPUAccessFlags = 0;
    bdesc.MiscFlags = 0;
    bdesc.StructureByteStride = 0;
    bdesc.Usage = D3D11_USAGE_DEFAULT;

    HR( gd3dDevice->CreateBuffer( &bdesc, NULL, &cb_PS ) );
}

void D3D11Pad::GlobalExit() {
    REL( IL_D3DXVECTOR3 );
    REL( VB );
    REL( IB );
    REL( VS_Ellipse );
    REL( VS_WideRect );
    REL( VS_Line );
    REL( VS_Fill );
    REL( PS_Draw );
    REL( PS_Fill );
    REL( Circle_low );
    REL( Circle_high );
    REL( cb_VS );
    REL( cb_PS );
    if (nfcache)
    {
        for(auto i = 0; i < nfcache; i++)
        {
            if (fcache[i].pFont)
            {
                delete fcache[i].pFont;
            }
        }
        nfcache = 0;
    }
}
#pragma endregion vertex buffers, shaders, layouts...

D3D11Pad::D3D11Pad(D3D11Config *pConfig,
		           Scene *pScene,
		           SURFHANDLE surf,
                   TextureMgr * textureMgr,
                   bool _static ) : oapi::Sketchpad( surf )
{
    m_textureMgr = textureMgr;

    cfont = NULL;
    cpen = NULL;
    cbrush = NULL;

    TEX = (Texture*)surf;
    if( TEX ) {        //surface
        Tex = TEX->GetTex();
        RTV_Tex = *TEX->GetRTV();
        TEX->GetSize( &width, &height );
    }
    else {            //2D buffer
        Tex = pScene->PBuffer;
		RTV_Tex = pScene->PBuffer_RTV;
        width = pConfig->cWidth;
        height = pConfig->cHeight;
    }
    iCtx->OMSetRenderTargets( 1, &RTV_Tex, NULL );
    D3DXMatrixOrthoOffCenterLH( &VSCB.M, 0.0f, (float)width, (float)height, 0.0f, 0.0f, 1.0f );

    D3D11_VIEWPORT VP;
    VP.Width = (float)width;
    VP.Height = (float)height;
    VP.MinDepth = 0.0f;
    VP.MaxDepth = 1.0f;
    VP.TopLeftX = 0;
    VP.TopLeftY = 0;
    iCtx->RSSetViewports( 1, &VP );

    origx = 1;
    origy = 1;
    cx = cy = 0;
    BkMode = TRANSPARENT;
    HAlign = TA_LEFT;
    VAlign = TA_TOP;

    TextColor =        D3DXCOLOR( 0.0f, 1.0f, 0.0f, 1.0f );
    PenColor =        D3DXCOLOR( 0.0f, 1.0f, 0.0f, 1.0f );
    BrushColor =    D3DXCOLOR( 0.0f, 1.0f, 0.0f, 1.0f );
    BkColor =        D3DXCOLOR( 0.0f, 0.0f, 0.0f, 1.0f );

    cfont = def_Font;

    hDC = NULL;
}

D3D11Pad::~D3D11Pad() 
{
    //nothing to do
    if( hDC ) {
        SURFHANDLE surf = this->GetSurface();
        m_textureMgr->ReleaseSurfaceDC( hDC, (Texture*)surf );
        hDC = NULL;
    }
}

oapi::Font *D3D11Pad::SetFont( oapi::Font *font ) const
{
    oapi::Font *pfont = cfont;
    if( font )        cfont = font;
    else            cfont = def_Font;
    return pfont;
}

oapi::Pen *D3D11Pad::SetPen( oapi::Pen *pen ) const
{
    oapi::Pen *ppen = cpen;
    if( pen )        cpen = pen;
    else            cpen = NULL;
    if( cpen )
        PenColor = ((D3D11PadPen*)cpen)->fcolor;
    return ppen;
}

oapi::Brush *D3D11Pad::SetBrush( oapi::Brush *brush ) const
{
    oapi::Brush *pbrush = cbrush;
    cbrush = brush;
    if( brush )        BrushColor = ((D3D11PadBrush*)cbrush)->fcolor;
    else            BrushColor = D3DXCOLOR( (DWORD)0 );
    return pbrush;
}

void D3D11Pad::SetTextAlign( TAlign_horizontal TA_h, TAlign_vertical TA_v ) {
    HAlign = VAlign = 0;

    switch( TA_h ) {
    case LEFT:
        HAlign |= TA_LEFT;
        break;
    case CENTER:
        HAlign |= TA_CENTER;
        break;
    case RIGHT:
        HAlign |= TA_RIGHT;
        break;
    }

    switch( TA_v ) {
    case TOP:
        VAlign |= TA_TOP;
        break;
    case BASELINE:
        VAlign |= TA_BASELINE;
        break;
    case BOTTOM:
        VAlign |= TA_BOTTOM;
        break;
    }
}

DWORD D3D11Pad::SetTextColor( DWORD color ) {
    if( !(color & 0xFF000000) )
        color |= 0xFF000000;
    D3DXCOLOR prev = TextColor;
    TextColor = D3DXCOLOR( color );
    D3DXCOLORSWAP( &TextColor );
    D3DXCOLORSWAP( &prev );
    return prev;
}

DWORD D3D11Pad::SetBackgroundColor( DWORD color ) {
    if( !(color & 0xFF000000) )
        color |= 0xFF000000;
    D3DXCOLOR prev = BkColor;
    BkColor = D3DXCOLOR( color );
    D3DXCOLORSWAP( &BkColor );
    D3DXCOLORSWAP( &prev );
    return prev;
}

void D3D11Pad::SetBackgroundMode( BkgMode bk_mode ) {
    switch( bk_mode ) {
    case BK_TRANSPARENT:
        BkMode = TRANSPARENT;
        break;
    case BK_OPAQUE:
        BkMode = OPAQUE;
        break;
    }
}

DWORD D3D11Pad::GetCharSize() {
    TEXTMETRICA tm;
    if( !cfont )        return 0;
    ((D3D11PadFont*)cfont)->pFont->GetD3D11TextMetrics( &tm );
    return MAKELONG( tm.tmHeight - tm.tmInternalLeading, tm.tmAveCharWidth );
}

DWORD D3D11Pad::GetTextWidth( const char *str, int len ) {
    if( !cfont )        return 0;
    return DWORD( ((D3D11PadFont*)cfont)->pFont->Length2( str, len ) );
}

void D3D11Pad::SetOrigin( int x, int y ) {
    origx = x;
    origy = y;
}

bool D3D11Pad::HasPen() {
    if( !cpen )        return false;
    if( ((D3D11PadPen*)cpen)->style == PS_NULL )    return false;
    return true;
}

bool D3D11Pad::IsDashed() {
    if( !cpen )        return false;
    if( ((D3D11PadPen*)cpen)->style == PS_DOT )        return true;
    return false;
}

bool D3D11Pad::HasWidePen() {
    if( !cpen )        return false;
    if( ((D3D11PadPen*)cpen)->width > 1 )    return true;
    return false;
}

bool D3D11Pad::HasBrush() {
    return (cbrush != NULL);
}

bool D3D11Pad::Text( int x, int y, const char *str, int len ) {
    if( !cfont )        return false;

    D3D11Text *pText = ((D3D11PadFont*)cfont)->pFont;

    switch( HAlign ) {
    default:
    case TA_LEFT:
        pText->SetTextHAlign( 0 );
        break;
    case TA_CENTER:
        pText->SetTextHAlign( 1 );
        break;
    case TA_RIGHT:
        pText->SetTextHAlign( 2 );
        break;
    }

    switch( VAlign ) {
    default:
    case TA_TOP:
        pText->SetTextVAlign( 0 );
        break;
    case TA_BASELINE:
        pText->SetTextVAlign( 1 );
        break;
    case TA_BOTTOM:
        pText->SetTextVAlign( 2 );
        break;
    }

/*    if( hDC ) {
        HFONT hFont = (HFONT)SelectObject( hDC, ((D3D11PadFont*)pText)->hFont );
        SetBkMode( hDC, BkMode );
        ::SetTextAlign( hDC, TA_LEFT );
        SetBkColor( hDC, (COLORREF)0 );
        TextOutA( hDC, x, y, str, len );
        return true;
    }*/

    pText->SetRotation( ((D3D11PadFont*)cfont)->rotation );

    if( BkMode == OPAQUE )        pText->Print( &TextColor, origx + x - 1, origy + y - 1, str, len, &VSCB.M, &BkColor );
    else                        pText->Print( &TextColor, origx + x - 1, origy + y - 1, str, len, &VSCB.M );

    return true;
}

void D3D11Pad::Pixel( int x, int y, DWORD col ) {
    if( x > (int)width-1 || y > (int)height-1 )
        return;

    D3DXCOLOR Color( col );
    D3DXCOLORSWAP( &Color );

    iCtx->ClearRenderTargetView( RTV_Tex_2x2, Color );

    D3D11_BOX Box;
    Box.back = 1;
    Box.front = 0;
    Box.top = 0;
    Box.left = 0;
    Box.right = 1;
    Box.bottom = 1;

    iCtx->CopySubresourceRegion( Tex, 0, x, y, 0, Tex_2x2, 0, &Box );
}

void D3D11Pad::MoveTo( int x, int y ) {
    cx = origx + x;
    cy = origy + y;
}

void D3D11Pad::LineTo( int x, int y ) {
    if( !HasPen() )    return;

    D3DXVECTOR3 pts[2];
    pts[0] = D3DXVECTOR3( (float)cx, (float)cy, 0.0f );
    pts[1] = D3DXVECTOR3( float(origx + x), float(origy + y), 0.0f );
    Lines( pts, 1 );
    cx = origx + x;
    cy = origy + y;
}

void D3D11Pad::Line( int x0, int y0, int x1, int y1 ) {
    if( !HasPen() )    return;

    x0 += origx;
    y0 += origy;
    x1 += origx;
    y1 += origy;
    D3DXVECTOR3 pts[2];
    pts[0] = D3DXVECTOR3( (float)x0, (float)y0, 0.0f );
    pts[1] = D3DXVECTOR3( (float)x1, (float)y1, 0.0f );
    Lines( pts, 1 );
    cx = x1;
    cy = y1;
}

void D3D11Pad::Rectangle( int l, int t, int r, int b ) {
    l += origx;
    t += origy;
    r += origx;
    b += origy;
    r--;
    b--;
    D3D11PadPen *pen = (D3D11PadPen*)cpen;
    if( HasWidePen() )    Rectangle3( float(l+r)*0.5f, float(t+b)*0.5f, float(r-l), float(b-t), pen->width );
    else                Rectangle2( float(l), float(t), float(r), float(b) );
}

void D3D11Pad::Ellipse( int l, int t, int r, int b ) {
    l += origx;
    t += origy;
    r += origx;
    b += origy;
    r--;
    b--;
    Ellipse2( float(l), float(t), float(r), float(b) );
}

void D3D11Pad::Polygon( const oapi::IVECTOR2 *pt, int npt ) {
    if( npt < 3 )    return;

    bool bIB = false;
    int nIdx = 0, j;

    WORD *Idx = NULL;
    D3DXVECTOR3 *Vtx = new D3DXVECTOR3 [npt+1];
    for( j = 0; j < npt; j++ )
        Vtx[j].x = float( pt[j].x + origx ),    Vtx[j].y = float( pt[j].y + origy );

    Vtx[npt].x = float( pt[0].x + origx ),    Vtx[npt].y = float( pt[0].y + origy );

    if( IsDashed() ) {
        Vtx[0].z = 0.0f;
        for( j = 1; j < npt; j++ ) {
            D3DXVECTOR3 d = Vtx[j] - Vtx[j-1];
            d.z = 0.0f;
            Vtx[j].z = Vtx[j-1].z + D3DXVec3Length( &d );
        }
        PSCB.Dash = 1;
    }
    else
        PSCB.Dash = 0;

    if( HasBrush() ) {
        Idx = new WORD [(npt-2)*3];
        nIdx = CreatePolyIndexList( Vtx, npt, Idx );
        if( nIdx > (int)ib_size )
            ExtendIndexBuffer( nIdx );
    }

    if( (npt+1) > (int)vb_size )
        ExtendVertexBuffer( npt+1 );

    D3D11_MAPPED_SUBRESOURCE SRes;
    ZeroMemory( &SRes, sizeof(SRes) );
    iCtx->Map( VB, 0, D3D11_MAP_WRITE_DISCARD, 0, &SRes );
    memcpy( SRes.pData, Vtx, (npt+1)*12 );
    iCtx->Unmap( VB, 0 );

    iCtx->IASetVertexBuffers( 0, 1, &VB, &SkpStride, &VBOffset );
    iCtx->VSSetShader( VS_Line, NULL, NULL );
    iCtx->PSSetShader( PS_Draw, NULL, NULL );

    SetRParams();

    if( nIdx ) {    //indexed triangles
        iCtx->IASetPrimitiveTopology( D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST );

        iCtx->Map( IB, 0, D3D11_MAP_WRITE_DISCARD, 0, &SRes );
        memcpy( SRes.pData, Idx, nIdx*2 );
        iCtx->Unmap( IB, 0 );

        PSCB.COL = BrushColor;
        iCtx->UpdateSubresource( cb_PS, 0, NULL, &PSCB, 0, 0 );
        iCtx->PSSetConstantBuffers( 0, 1, &cb_PS );

        iCtx->IASetIndexBuffer( IB, DXGI_FORMAT_R16_UINT, 0 );
        iCtx->DrawIndexed( nIdx, 0, 0 );
    }

    if( HasPen() ) {    //line
        iCtx->IASetPrimitiveTopology( D3D_PRIMITIVE_TOPOLOGY_LINESTRIP );

        PSCB.COL = PenColor;
        iCtx->UpdateSubresource( cb_PS, 0, NULL, &PSCB, 0, 0 );
        iCtx->PSSetConstantBuffers( 0, 1, &cb_PS );

        iCtx->Draw( npt+1, 0 );
    }

    if( Idx )    delete [ ] Idx;
    delete [ ] Vtx;
}

void D3D11Pad::ExtendVertexBuffer( UINT new_size ) {
    REL( VB );

    D3D11_BUFFER_DESC bdesc;
    ZeroMemory( &bdesc, sizeof(bdesc) );
    bdesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    bdesc.ByteWidth = new_size*12;
    bdesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    bdesc.MiscFlags = 0;
    bdesc.StructureByteStride = 0;
    bdesc.Usage = D3D11_USAGE_DYNAMIC;

    HR( gd3dDevice->CreateBuffer( &bdesc, NULL, &VB ) );

    vb_size = new_size;
}

void D3D11Pad::ExtendIndexBuffer( UINT new_size ) {
    REL( IB );

    D3D11_BUFFER_DESC bdesc;
    ZeroMemory( &bdesc, sizeof(bdesc) );
    bdesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
    bdesc.ByteWidth = 2*new_size;
    bdesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    bdesc.MiscFlags = 0;
    bdesc.StructureByteStride = 0;
    bdesc.Usage = D3D11_USAGE_DYNAMIC;

    HR( gd3dDevice->CreateBuffer( &bdesc, NULL, &IB ) );

    ib_size = new_size;
}

void D3D11Pad::Polyline( const oapi::IVECTOR2 *pt, int npt ) {
    if( !HasPen() )        return;

    int j;
    D3DXVECTOR3 *Vtx = new D3DXVECTOR3 [npt];
    for( j = 0; j < npt; j++ )
        Vtx[j].x = float(pt[j].x + origx),    Vtx[j].y = float(pt[j].y + origy);

    if( IsDashed() ) {
        Vtx[0].z = 0.0f;
        for( j = 1; j < npt; j++ ) {
            D3DXVECTOR3 d = Vtx[j] - Vtx[j-1];
            d.z = 0.0f;
            Vtx[j].z = Vtx[j-1].z + D3DXVec3Length( &d );
        }
        PSCB.Dash = 1;
    }
    else
        PSCB.Dash = 0;

//update vertex buffer and extend if needed
    if( npt > (int)vb_size )
        ExtendVertexBuffer( npt );
    D3D11_MAPPED_SUBRESOURCE SRes;
    ZeroMemory( &SRes, sizeof(SRes) );
    iCtx->Map( VB, 0, D3D11_MAP_WRITE_DISCARD, 0, &SRes );
    memcpy( SRes.pData, Vtx, (npt+1)*12 );
    iCtx->Unmap( VB, 0 );

//draw
    SetRParams();

    PSCB.COL = PenColor;
    iCtx->UpdateSubresource( cb_PS, 0, NULL, &PSCB, 0, 0 );
    iCtx->PSSetConstantBuffers( 0, 1, &cb_PS );

    iCtx->IASetPrimitiveTopology( D3D_PRIMITIVE_TOPOLOGY_LINESTRIP );
    iCtx->IASetVertexBuffers( 0, 1, &VB, &SkpStride, &VBOffset );
    iCtx->VSSetShader( VS_Line, NULL, NULL );
    iCtx->PSSetShader( PS_Draw, NULL, NULL );

    iCtx->Draw( npt, 0 );

    delete [ ] Vtx;
}

void D3D11Pad::SetRParams( D3DXVECTOR4 *data ) {
    iCtx->IASetInputLayout( IL_D3DXVECTOR3 );
    iCtx->RSSetState( RS_CullNone_Solid );
    iCtx->OMSetBlendState( BS_SrcAlpha, D3DXCOLOR( 1, 1, 1, 1 ), 0xFFFFFFFF );
    iCtx->OMSetDepthStencilState( DSS_NoDepth_NoStencil, 0 );

    if( data )    VSCB.Data = *data;
    iCtx->UpdateSubresource( cb_VS, 0, NULL, &VSCB.M, 0, 0 );
    iCtx->VSSetConstantBuffers( 0, 1, &cb_VS );
}

void D3D11Pad::Lines( D3DXVECTOR3 *pVert, int count ) {
    if( IsDashed() ) {
        pVert[0].z = 0.0f;
        for( UINT j = 1; j < ((UINT)count << 1); j++ ) {
            D3DXVECTOR3 d = pVert[j] - pVert[j-1];
            d.z = 0.0f;
            pVert[j].z = pVert[j-1].z + D3DXVec3Length( &d );
        }
        PSCB.Dash = 1;
    }
    else
        PSCB.Dash = 0;

    if( HasPen() ) {
        SetRParams();

        if( count*2 > (int)vb_size )
            ExtendVertexBuffer( count*2 );


    /*    ZeroMemory( &SRes, sizeof(SRes) );
        iCtx->Map( VB, 0, D3D11_MAP_WRITE_DISCARD, 0, &SRes );
        memcpy( SRes.pData, pVert, count*2*12 );
        iCtx->Unmap( VB, 0 );*/

        D3D11_MAPPED_SUBRESOURCE SRes;
        ZeroMemory( &SRes, sizeof(SRes) );
        iCtx->Map( VB, 0, D3D11_MAP_WRITE_DISCARD, 0, &SRes );
        memcpy( SRes.pData, pVert, count*2*12 );
        iCtx->Unmap( VB, 0 );

    //    iCtx->OMSetRenderTargets( 1, TEX->GetRTV(), NULL );
        iCtx->IASetPrimitiveTopology( D3D_PRIMITIVE_TOPOLOGY_LINELIST );
        iCtx->IASetVertexBuffers( 0, 1, &VB, &SkpStride, &VBOffset );

        PSCB.COL = PenColor;
        iCtx->UpdateSubresource( cb_PS, 0, NULL, &PSCB, 0, 0 );
        iCtx->PSSetConstantBuffers( 0, 1, &cb_PS );

        iCtx->VSSetShader( VS_Line, NULL, NULL );
        iCtx->PSSetShader( PS_Draw, NULL, NULL );
        iCtx->IASetVertexBuffers( 0, 1, &VB, &SkpStride, &VBOffset );

        iCtx->Draw( count*2, 0 );
    }
}

void D3D11Pad::Ellipse2( float l, float t, float r, float b ) {
    bool bLowRes = true;
    float
        w = r - l,
        h = b - t;

//    iCtx->OMSetRenderTargets( 1, TEX->GetRTV(), NULL );
    iCtx->IASetPrimitiveTopology( D3D_PRIMITIVE_TOPOLOGY_LINESTRIP );
    if( w > 32.0f || h > 32.0f ) {
        bLowRes = false;
        iCtx->IASetVertexBuffers( 0, 1, &Circle_high, &SkpStride, &VBOffset );
    }
    else
        iCtx->IASetVertexBuffers( 0, 1, &Circle_low, &SkpStride, &VBOffset );

    iCtx->VSSetShader( VS_Ellipse, NULL, NULL );
    iCtx->PSSetShader( PS_Draw, NULL, NULL );

    SetRParams( &D3DXVECTOR4( l, t, w, h ) );

    PSCB.Dash = 0;
    if( HasBrush() ) {
        PSCB.COL = BrushColor;
        iCtx->UpdateSubresource( cb_PS, 0, NULL, &PSCB, 0, 0 );
        iCtx->PSSetConstantBuffers( 0, 1, &cb_PS );

        iCtx->VSSetShader( VS_Ellipse, NULL, NULL );
        iCtx->PSSetShader( PS_Draw, NULL, NULL );

        if( bLowRes )
            iCtx->Draw( 15, 1 );
        else
            iCtx->Draw( 63, 1 );
    }

    if( IsDashed() )
        PSCB.Dash = 1;

    if( HasPen() ) {
        PSCB.COL = PenColor;
        iCtx->UpdateSubresource( cb_PS, 0, NULL, &PSCB, 0, 0 );
        iCtx->PSSetConstantBuffers( 0, 1, &cb_PS );

        iCtx->VSSetShader( VS_Ellipse, NULL, NULL );
        iCtx->PSSetShader( PS_Draw, NULL, NULL );

        if( bLowRes )
            iCtx->Draw( 16, 1 );
        else
            iCtx->Draw( 64, 1 );
    }
}

void D3D11Pad::Rectangle2( float l, float t, float r, float b ) {
    static WORD Idx[6] = { 0, 1, 2, 0, 2, 3 };

    static D3DVECTOR Vtx[5] = {
        { 0.0f, 0.0f, 0.0f},
        { 1.0f, 0.0f, 0.0f},
        { 1.0f, 1.0f, 0.0f},
        { 0.0f, 1.0f, 0.0f},
        { 0.0f, 0.0f, 0.0f}
    };

    SetRParams( &D3DXVECTOR4( l, t, r-l, b-t ) );
    PSCB.Dash = 0;

    D3D11_MAPPED_SUBRESOURCE SRes;
    ZeroMemory( &SRes, sizeof(SRes) );
    iCtx->Map( VB, 0, D3D11_MAP_WRITE_DISCARD, 0, &SRes );
    memcpy( SRes.pData, Vtx, 5*12 );
    iCtx->Unmap( VB, 0 );

//    iCtx->OMSetRenderTargets( 1, TEX->GetRTV(), NULL );
    iCtx->IASetVertexBuffers( 0, 1, &VB, &SkpStride, &VBOffset );
    iCtx->VSSetShader( VS_Ellipse, NULL, NULL );
    iCtx->PSSetShader( PS_Draw, NULL, NULL );

    if( HasBrush() ) {
        ZeroMemory( &SRes, sizeof(SRes) );
        iCtx->Map( IB, 0, D3D11_MAP_WRITE_DISCARD, 0, &SRes );
        memcpy( SRes.pData, Idx, 12 );
        iCtx->Unmap( IB, 0 );

        PSCB.COL = BrushColor;
        iCtx->UpdateSubresource( cb_PS, 0, NULL, &PSCB, 0, 0 );
        iCtx->PSSetConstantBuffers( 0, 1, &cb_PS );

        iCtx->IASetPrimitiveTopology( D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
        iCtx->IASetIndexBuffer( IB, DXGI_FORMAT_R16_UINT, 0 );

        iCtx->DrawIndexed( 6, 0, 0 );
    }

    if( HasPen() ) {
        PSCB.COL = PenColor;
        iCtx->UpdateSubresource( cb_PS, 0, NULL, &PSCB, 0, 0 );
        iCtx->PSSetConstantBuffers( 0, 1, &cb_PS );

        iCtx->IASetPrimitiveTopology( D3D_PRIMITIVE_TOPOLOGY_LINESTRIP );

        iCtx->Draw( 5, 0 );
    }
}

void D3D11Pad::Rectangle3( float x, float y, float w, float h, int width ) {
    static WORD o_idx[24] = { 0, 1, 2,  2, 1, 3,  2, 3, 4,  4, 3, 5,  4, 5, 6,  6, 5, 7,  6, 7, 0,  0, 7, 1 };
    static WORD f_idx[6]  = { 1, 5, 3,  1, 7, 5 };

    float i = float( width/2 );
    float u = float( width - int(i+1) );

    D3DVECTOR Vtx[8] = {
        { -1.0f,  1.0f,  u },
        { -1.0f,  1.0f, -i },
        {  1.0f,  1.0f,  u },
        {  1.0f,  1.0f, -i },
        {  1.0f, -1.0f,  u },
        {  1.0f, -1.0f, -i },
        { -1.0f, -1.0f,  u },
        { -1.0f, -1.0f, -i }
    };

    SetRParams( &D3DXVECTOR4( x, y, w*0.5f, h*0.5f ) );
    PSCB.Dash = 0;

    D3D11_MAPPED_SUBRESOURCE SRes;
    ZeroMemory( &SRes, sizeof(SRes) );
    iCtx->Map( VB, 0, D3D11_MAP_WRITE_DISCARD, 0, &SRes );
    memcpy( SRes.pData, Vtx, 8*12 );
    iCtx->Unmap( VB, 0 );

    iCtx->IASetVertexBuffers( 0, 1, &VB, &SkpStride, &VBOffset );
    iCtx->VSSetShader( VS_WideRect, NULL, NULL );
    iCtx->PSSetShader( PS_Fill, NULL, NULL );

    if( HasBrush() ) {
        ZeroMemory( &SRes, sizeof(SRes) );
        iCtx->Map( IB, 0, D3D11_MAP_WRITE_DISCARD, 0, &SRes );
        memcpy( SRes.pData, f_idx, 12 );
        iCtx->Unmap( IB, 0 );

        PSCB.COL = BrushColor;
        iCtx->UpdateSubresource( cb_PS, 0, NULL, &PSCB, 0, 0 );
        iCtx->PSSetConstantBuffers( 0, 1, &cb_PS );

        iCtx->IASetPrimitiveTopology( D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
        iCtx->IASetIndexBuffer( IB, DXGI_FORMAT_R16_UINT, 0 );

        iCtx->DrawIndexed( 6, 0, 0 );
    }

    if( HasPen() ) {
        ZeroMemory( &SRes, sizeof(SRes) );
        iCtx->Map( IB, 0, D3D11_MAP_WRITE_DISCARD, 0, &SRes );
        memcpy( SRes.pData, o_idx, 48 );
        iCtx->Unmap( IB, 0 );

        PSCB.COL = PenColor;
        iCtx->UpdateSubresource( cb_PS, 0, NULL, &PSCB, 0, 0 );
        iCtx->PSSetConstantBuffers( 0, 1, &cb_PS );

        iCtx->IASetPrimitiveTopology( D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
        iCtx->IASetIndexBuffer( IB, DXGI_FORMAT_R16_UINT, 0 );

        iCtx->DrawIndexed( 24, 0, 0 );
    }
}

int D3D11Pad::CheckTriangle( short x, const D3DXVECTOR3 *pt, const WORD *Idx, float hd, short npt, bool bSharp ) {
    WORD A = Idx[x];
    WORD B = Idx[mod( x-1, npt )];
    WORD C = Idx[mod( x+1, npt )];

    float bx = pt[B].x - pt[A].x;
    float by = pt[B].y - pt[A].y;
    float ax = pt[C].x - pt[A].x;
    float ay = pt[C].y - pt[A].y;

    if( (bx*ay - by*ax)*hd > 0 )    return 0;    // Check handiness

    float aa = ax*ax + ay*ay;            // dot(a,a)
    float ab = ax*bx + ay*by;            // dot(a,b)
    float bb = bx*bx + by*by;            // dot(b,b)

    float qw = fabs(ab) / sqrt(aa*bb);    // abs(cos(a,b))
    if( bSharp && qw > 0.9f )    return 0;    // Bad Ear
    if( qw > 0.9999f )    return 0;        // All three points are lined up

    float id = 1.0f / (aa * bb - ab * ab);

    for( int i = 0; i < npt; i++ ) {

        WORD P = Idx[i];

        if( P == B || P == A || P == C )    continue;

        float cx = pt[P].x - pt[A].x;
        float cy = pt[P].y - pt[A].y;
        float ac = ax*cx + ay*cy;     if( ac < 0 )    continue;
        float bc = bx*cx + by*cy;     if( bc < 0 )    continue;
        float u  = (bb*ac - ab*bc) * id;
        float v  = (aa*bc - ab*ac) * id;

        // Check if the point is inside the triangle
        // NOTE: Having u+v slightly above 1.0 is a bad condition, should find a better ear.
        if( ( u > 0.0f ) && ( v > 0.0f ) && ( (u+v) < 1.0f ) )    return 0;
    }

    return 1; // It's an ear
}

int D3D11Pad::CreatePolyIndexList( const D3DXVECTOR3 *pt, short npt, WORD *Out ) {
    if( npt < 3 )
        return 0;
    if( npt == 3 ) {
        Out[0] = 0;
        Out[1] = 1;
        Out[2] = 2;
        return 3;
    }

    short idx = 0;        // Number of indices written in the output
    short x = npt-1;    // First ear to test is the last one in the list
    bool bSharp = true; // Avoid sharp ears

    // Build initial index list
    WORD *In = new WORD [npt];
    for( int i = 0; i < npt; i++ )    In[i] = i;
    float sum = 0;
    int k = npt-1;
    for( int i = 0; i < k; i++ )
        sum += (pt[i].x*pt[(i+1)%k].y - pt[(i+1)%k].x*pt[i].y);

    if( sum > 0 )        sum = 1.0;
    else                sum = -1.0;

    while( npt > 3 ) {
        switch( CheckTriangle( x, pt, In, sum, npt, bSharp ) ) {
            case 0:
            {
                x--;
                if( x < 0 ) { // Restart
                    if( !bSharp ) {
                        delete [ ] In;
                        return idx;
                    }
                    bSharp = false;
                    x = npt-1;
                }
                break;
            }
            case 1:
            {
                WORD a = Out[idx] = In[mod( x-1, npt )];    idx++;
                WORD b = Out[idx] = In[mod( x, npt )];        idx++;
                WORD c = Out[idx] = In[mod( x+1, npt )];    idx++;
                npt--;
                for( int i = x; i < npt; i++ ) In[i] = In[i+1];
                x = mod( x-1, npt );
                break;
            }
        }
    }

    Out[idx] = In[0]; idx++;
    Out[idx] = In[1]; idx++;
    Out[idx] = In[2]; idx++;
    delete [ ] In;
    return idx;
}

HDC D3D11Pad::GetDC() {
/*    if( !hDC ) {
        SURFHANDLE surf = this->GetSurface();
        hDC = TM->GetSurfaceDC( (Texture*)surf );
    }
    return hDC;*/
    return NULL;
}
/*
void D3D11Pad::RelDC() {
    if( hDC ) {
        SURFHANDLE surf = this->GetSurface();
        TM->ReleaseSurfaceDC( hDC, (Texture*)surf );
        hDC = NULL;
    }
}
*/
//==========================================================================
//                                D3D11Font
//==========================================================================

D3D11PadFont::D3D11PadFont( int height, bool prop, const char *face, Style style, int orientation ) : oapi::Font( height, prop, face, style, orientation ) {
    fonts_allocated++;

    char *def_fixedface = "Courier New";
    char *def_sansface = "Arial";
    char *def_serifface = "Times New Roman";

    if( face[0] != '*' ) {
        if( !_stricmp( face, "fixed" ) )
            face = def_fixedface;
        else if( !_stricmp( face, "sans" ) )
            face = def_sansface;
        else if( !_stricmp( face, "serif" ) )
            face = def_serifface;
        else if( _stricmp( face, def_fixedface ) && _stricmp( face, def_sansface ) && _stricmp( face, def_serifface ) )
            face = (prop ? def_sansface : def_fixedface);
    }
    else
        face++;

    pFont = NULL;
    hFont = NULL;
    bDelete = false;

    if( orientation )    rotation = float(orientation)*0.1f;
    else                rotation = 0.0f;

    // Browse cache ---------------------------------------------------
    //

    for( int j = 0; j < nfcache; j++ ) {
        if( fcache[j].height != height )
            continue;
        if( fcache[j].style != style )
            continue;
        if( fcache[j].prop != prop )
            continue;
        if( _stricmp( fcache[j].face, face ) )
            continue;
        pFont = fcache[j].pFont;
        if( !orientation )
            hFont = fcache[j].hFont;
    }

    int weight = (style & BOLD ? FW_BOLD : FW_NORMAL);
    DWORD italic = (style & ITALIC ? TRUE : FALSE);
    DWORD underline = (style & UNDERLINE ? TRUE : FALSE);

    // Create Windows GDI Font --------------------------
    //
    if( !hFont ) {
        hFont = CreateFontA( height, 0, orientation, orientation, weight, italic, underline, 0, 0, 3, 2, 3, 49, face );
        if( !hFont ) {
            face = (prop ? def_sansface : def_fixedface);
            hFont = CreateFontA( height, 0, orientation, orientation, weight, italic, underline, 0, 0, 3, 2, 1, 49, face );
        }
        if( orientation )
            bDelete = true;
    }

    // Create Direct3D accelerated font ------------------
    //
    if( !pFont ) {
        HFONT hNew = CreateFontA( height, 0, 0, 0, weight, italic, underline, 0, 0, 3, 2, 3, 49, face );

        pFont = new D3D11Text();
        pFont->Init( hNew, 255 );

        DeleteObject( hNew );

        pFont->SetRotation( rotation );

        if( nfcache > 120 )
            oapiWriteLog( "cache is full" );

        // Fill the cache --------------------------------
        //

        if( orientation )    fcache[nfcache].hFont = NULL;    // Do not cache rotated hFont for Windows GDI
        else                fcache[nfcache].hFont = hFont;

        fcache[nfcache].pFont = pFont;
        fcache[nfcache].height = height;
        fcache[nfcache].style = style;
        fcache[nfcache].prop = prop;
        strcpy_s( fcache[nfcache].face, 32, face );
        nfcache++;
    }
}

D3D11PadFont::~D3D11PadFont() {
    if( pFont )
        pFont->SetRotation( 0.0f );
    fonts_allocated--;
    if( hFont && bDelete )
        DeleteObject( hFont );
}

//==========================================================================
//                                D3D11Pen
//==========================================================================

D3D11PadPen::D3D11PadPen( int s, int w, DWORD col ) : oapi::Pen( style, width, col )
{
    pens_allocated++;

    switch( s ) {
    case 0:
        style = PS_NULL;
        break;
    case 2:
        style = PS_DOT;
        break;
    default:
        style = PS_SOLID;
        break;
    }

    width = w;
    if( width < 1 )
        width = 1;
    hPen = CreatePen( style, width, COLORREF( col & 0xFFFFFF ) );
    if( !(col & 0xFF000000) )
        col |= 0xFF000000;
    fcolor = D3DXCOLOR( col );
    D3DXCOLORSWAP( &fcolor );
}

D3D11PadPen::~D3D11PadPen() {
    pens_allocated--;
    DeleteObject( hPen );
}

//==========================================================================
//                                D3D11Brush
//==========================================================================

D3D11PadBrush::D3D11PadBrush( DWORD col ) : oapi::Brush( col )
{
    brushes_allocated++;

    hBrush = CreateSolidBrush( COLORREF( col & 0xFFFFFF ) );
    if( !(col & 0xFF000000) )
        col |= 0xFF000000;
    fcolor = D3DXCOLOR( col );
    D3DXCOLORSWAP( &fcolor );
}

D3D11PadBrush::~D3D11PadBrush() {
    brushes_allocated--;
    DeleteObject( hBrush );
}
